package com.specter.dev.docs.execute;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import java.util.regex.Pattern;

import com.specter.dev.docs.builder.GenConfiguration.JApiInput;
import com.specter.utils.StringUtils;
import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaAnnotation;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.JavaParameter;
import com.thoughtworks.qdox.model.expression.AnnotationValue;
import com.thoughtworks.qdox.model.expression.AnnotationValueList;

/**
 * Note:通过扫描实体包来完成实体类搜索<br/>
 * <br/>
 * <b> &lt;link typ="spring" src="cn.liangthink.*.entity;*.entity" tgt="a,b"
 * fix="%s+Entity" /&gt; </b>
 * 
 * @author Liang.Wang
 * @version 1.0
 * 
 */
public class JApiInputExe4Spring implements JApiInputExe {
	public static JavaProjectBuilder project = new JavaProjectBuilder();
	private static String CONTROLLER = "org.springframework.stereotype.Controller";
	private static String REST_CONTROLLER = "org.springframework.web.bind.annotation.RestController";
	private static String REQUEST_MAPPING = "org.springframework.web.bind.annotation.RequestMapping";
	private static String REQUEST_BODY = "org.springframework.web.bind.annotation.RequestBody";
	private static String TREE_CHAR = "↳ ";// ↳ ∟ └──

	@Override
	public List<?> input(JApiInput input) {
		List<JApiInputExe.Controller> list = new ArrayList<>();
		Pattern tgt = Pattern.compile(input.getTgt());

		project.addSourceTree(new File(dir(input.getSrc())));
		project.getPackages().forEach(pkg -> {

			if (tgt.matcher(pkg.getName()).matches()) {// 按照规则匹配

				pkg.getClasses().forEach(cls -> {
					String default_uri = uri(input.getFix(), cls.getCanonicalName());

					for (JavaAnnotation a : cls.getAnnotations()) {
						if (a.getType().isA(CONTROLLER) || a.getType().isA(REST_CONTROLLER)) {
							Controller ctrl = this.ctrl(cls, default_uri);
							list.add(ctrl);
							break;
						}
					}

				});
			}
		});
		return list;
	}

	/**
	 * 解析单个Controller的java文件，封装成对应实体类。
	 * 
	 * @param action Controller对应java源码类
	 * 
	 * @return 对应的Controller实体
	 * 
	 */
	private JApiInputExe.Controller ctrl(JavaClass action, String uri) {
		JApiInputExe.Controller ctrl = new JApiInputExe.Controller();

		ctrl.setSince(tv(action.getTagByName("since"), null));
		ctrl.setAuthor(tv(action.getTagByName("author"), null));
		ctrl.setVersion(tv(action.getTagByName("version"), null));

		ctrl.setDesc(action.getComment());
		ctrl.setNote(tv(action.getTagByName("apiNote"), ctrl.getDesc()));

		ctrl.setUri(uri);// 构建默认地址

		for (JavaAnnotation a : action.getAnnotations()) {
			if (a.getType().isA(REQUEST_MAPPING)) {
				ctrl.setUri(av(a.getProperty("value"), ctrl.getUri()));
				break;
			}
		}

		List<JApiInputExe.ControllerMapping> mappings = new ArrayList<>();
		action.getMethods().forEach(m -> {
			ControllerMapping mapping = null;
			for (JavaAnnotation a : m.getAnnotations()) {
				if (a.getType().isA(REQUEST_MAPPING)) {
					mapping = this.mapping(m, a);
					break;
				}
			}
			mappings.add(mapping);
		});
		ctrl.setMappings(mappings.toArray(new JApiInputExe.ControllerMapping[mappings.size()]));
		return ctrl;
	}

	/**
	 * 解析Controller中配置RequestMapping的方法，封装成对应实体类。
	 * 
	 * @param method     源码对应的方法类
	 * @param annotation 源码对应的RequestMapping注解
	 * 
	 * @return 对应的Controller的请求实体
	 */
	private ControllerMapping mapping(JavaMethod method, JavaAnnotation annotation) {
		ControllerMapping mapping = new ControllerMapping();
		mapping.setDesc(method.getComment());
		mapping.setNote(tv(method.getTagByName("apiNote"), mapping.getDesc()));

		mapping.setUri(av(annotation.getProperty("value"), "/" + StringUtils.toViewName(method.getName())));
		mapping.setMethod(av(annotation.getProperty("method"), "GET").replace("RequestMethod.", ""));
		mapping.setConsumes(av(annotation.getProperty("consumes"), "application/x-www-form-urlencoded"));
		mapping.setProduces(av(annotation.getProperty("produces"), "*/*"));

		mapping.setParams(av(annotation.getProperty("params"), ""));
		mapping.setHeaders(av(annotation.getProperty("headers"), ""));

		/* 构建Request的部分 */
		this.setRequest(mapping, method);
		/* 构建Response的部分 */
		this.setResponse(mapping, method);

		return mapping;
	}

	/**
	 * 通过解析method对mapping中request部分进行设置
	 * 
	 * @param mapping 待设置的mapping
	 * @param method  待解析的method
	 */
	private void setRequest(ControllerMapping mapping, JavaMethod method) {
		/* 参数注释说明 @param */
		mapping.setParamNote("There is no @param tag in the comment !");
		if (method.getTagsByName("param").size() > 0) {
			StringBuffer sb = new StringBuffer("");
			method.getTagsByName("param").forEach(t -> {
				sb.append(tv(t, "no comments found.") + " ;\n");
			});
			sb.deleteCharAt(sb.length() - 1);
			mapping.setParamNote(sb.toString());
		}
		/* 参数数据列表 */
		List<JApiInputExe.ControllerEntity> params = new ArrayList<>();
		for (int i = 0; i < method.getParameters().size(); i++) {
			JavaParameter p = method.getParameters().get(i);
			// 检查@RequestBody的配置
			for (JavaAnnotation a : p.getAnnotations()) {
				if (a.getType().isA(REQUEST_BODY)) {
					mapping.setConsumes("application/json");
					break;
				}
			}
			JApiInputExe.ControllerEntity param = new JApiInputExe.ControllerEntity();
			param.setName(p.getName());
			param.setType(p.getType().getValue());
			param.setRequired(false);
			param.setDesc(tv(method.getTagsByName("param").get(i), "no comments found."));
			param.setMainClass(p.getCanonicalName());
			param.setDataClass(dv(method.getTagsByName("param").get(i)));

			int idx = method.getParameters().size() == 1 ? -1 : 0;// 用以控制第一层级
			setEntities(params, param, p.getJavaClass(), idx);
		}

		mapping.setParamEntities(params.toArray(new ControllerEntity[params.size()]));

		/* 请求参数样例 */
		mapping.setRequest("This Api With No Param! ");
		if (mapping.getParamEntities().length > 0) {
			mapping.setRequest(setJson(params));
		}
	}

	/**
	 * 通过解析method对mapping中response部分进行设置
	 * 
	 * @param mapping 待设置的mapping
	 * @param method  待解析的method
	 */
	private void setResponse(ControllerMapping mapping, JavaMethod method) {
		/* 响应注释说明 @return */
		mapping.setReturnNote(tv(method.getTagByName("return"), "There is no @return tag in the comment !"));

		/* 响应字段列表 */
		List<JApiInputExe.ControllerEntity> rets = new ArrayList<>();
		if (!method.getReturns().isVoid()) {
			JApiInputExe.ControllerEntity ret = new JApiInputExe.ControllerEntity();
			ret.setName("Response Object");
			ret.setType(method.getReturns().getName());
			ret.setRequired(true);
			ret.setDesc(mapping.getReturnNote());
			ret.setMainClass(method.getReturns().getCanonicalName());
			ret.setDataClass(dv(method.getTagByName("return")));

			setEntities(rets, ret, method.getReturns(), -1);
		}
		mapping.setReturnEntities(rets.toArray(new ControllerEntity[rets.size()]));

		/* 响应结果样例 */
		mapping.setResponse("This Api Return Void! ");
		if (!method.getReturns().isVoid()) {
			mapping.setResponse(setJson(rets));
		}
	}

	/**
	 * 对复合对象进行解析
	 * 
	 * @param list   对象列表
	 * @param entity 对象内容
	 * @param cls    对象类型(仅仅为了不通过project查找添加的参数，entity中已存的字符串名)
	 */
	private void setEntities(List<ControllerEntity> list, ControllerEntity entity, JavaClass cls, int i) {
		// JavaClass cls = project.getClassByName(entity.getMainClass());//直接传值
		entity.setName(lv(i) + entity.getName());
		// 1.如果对应的类为空或者为Object
		if (JApiInputExeUtils.isObject(cls)) {
			if (Objects.nonNull(entity.getDataClass())) {
				entity.setType(dv(StringUtils.substringAfter(entity.getDataClass(), "|")));
			}
			list.add(entity);

			ControllerEntity e = new ControllerEntity();
			e.setName("Inner Object");
			e.setType("Object");
			e.setRequired(entity.isRequired());

			if (Objects.isNull(entity.getDataClass())) {
				e.setName(lv(i + 1) + "Any Object");
				e.setDesc("内部对象为任意类型！");
				list.add(e);
			} else {
				JavaClass c = project.getClassByName(StringUtils.substringBefore(entity.getDataClass(), "|"));
				e.setType(c.getName());
				e.setDesc("内部对象为" + e.getType() + "的" + entity.getType() + "集合！");
				e.setMainClass(c.getCanonicalName());
				setEntities(list, e, c, i + 1);
			}
			return;
		}
		// 2.如果是忽略类型则退出
		if (JApiInputExeUtils.isIgnore(cls)) {
			return;
		}
		// 3.如果是基本类型接入列表并退出
		if (JApiInputExeUtils.isPrimitive(cls) || cls.isEnum()) {
			list.add(entity);
			return;
		}
		// 4.如果是Array,取类型后递归
		if (cls.isArray()) {
			entity.setType("Array");
			list.add(entity);
			// 准备下一级解析
			JavaClass c = cls.getComponentType();
			ControllerEntity e = new ControllerEntity();
			e.setName("Inner Object");
			e.setType(c.getName());
			e.setRequired(entity.isRequired());
			e.setDesc(c.getComment());
			e.setMainClass(c.getCanonicalName());
			setEntities(list, e, c, i + 1);
		}
		// 5.如果是Collection,取泛型后递归
		else if (JApiInputExeUtils.isCollection(cls)) {
			entity.setType("List");
			list.add(entity);
			// 准备下一级解析 如果是T或者?时返回java.lang.Object
			JavaClass c = project.getClassByName(JApiInputExeUtils.getGenericName(cls));
			ControllerEntity e = new ControllerEntity();
			e.setName("Inner Object");
			e.setType(c.getName());
			e.setRequired(entity.isRequired());
			e.setDesc(c.getComment());
			e.setMainClass(c.getCanonicalName());
			setEntities(list, e, c, i + 1);
		}
		// 6.如果是Map,取泛型后递归
		else if (JApiInputExeUtils.isMap(cls)) {
			entity.setType("Map");
			list.add(entity);
			// 准备下一级解析 如果是T或者?时返回java.lang.Object
			JavaClass c = project.getClassByName(JApiInputExeUtils.getGenericName(cls));
			ControllerEntity e = new ControllerEntity();
			e.setName("Inner Object");
			e.setType(c.getName());
			e.setRequired(entity.isRequired());
			e.setDesc(c.getComment());
			e.setMainClass(c.getCanonicalName());
			setEntities(list, e, c, i + 1);
		}
		// 7.如果是其他的Bean类型
		else {
			if (i >= 0) {
				list.add(entity);
			}
			for (JavaField f : cls.getFields()) {
				if (!JApiInputExeUtils.isIgnore(f)) {
					ControllerEntity e = new ControllerEntity();
					e.setName(f.getName());
					e.setType(f.getType().getName());
					e.setRequired(false);
					String comment = f.getComment();
					if (comment == null) {
						comment = f.getName().equals("success") ? "状态" : comment;
						comment = f.getName().equals("message") ? "消息" : comment;
					}
					e.setDesc(comment);
					e.setMainClass(f.getType().getCanonicalName());
					e.setDataClass(entity.getDataClass());
					setEntities(list, e, f.getType(), i + 1);// 递归
				}
			}
		}
	}

	private String setJson(List<ControllerEntity> list) {
		// 将List转换成Map层级对象，然后对层级Map转成JSON
		StringBuffer sb = new StringBuffer();
		boolean hasArray = false, hasObject = false;

		ControllerEntity start = list.get(0);
		if (JApiInputExeUtils.isCollection(start.getType())) {
			sb.append("[\n");
		} else {
			sb.append("{\n");
		}
		// --------------------------------------------------------
		ListIterator<ControllerEntity> item = list.listIterator();
		while (item.hasNext()) {
			ControllerEntity e = item.next();
			String itemName = e.getName().replace(TREE_CHAR, "").replace("　 ", "");

			// 1.如果是基本类型
			if (JApiInputExeUtils.isPrimitive(e.getType())) {
				sb.append("\t\t\"" + itemName + "\":");
				sb.append("\"" + e.getDesc() + "\",\n");
				if (!item.hasNext() || !JApiInputExeUtils.isPrimitive(list.get(item.nextIndex()).getType())) {
					if (hasObject) {
						sb.append("\t}");
						hasObject = false;
					}
					if (hasArray) {
						sb.append("]");
						hasArray = false;
					}
				}
			}
			// 2.集合类型Array/List
			else if ("List".equals(e.getType()) || "Array".equals(e.getType())) {
				if (item.previousIndex() > 0) {
					sb.append(",\n\t\"" + itemName + "\":[{\n");
				}
				hasArray = true;
			}
			// 3.对象类型 Map/Object/OtherType
			else {
				if (e.getName().contains("Inner Object")) {
					// sb.append("{\n");

				} else if (e.getName().contains("Any Object")) {
					sb.append("\t\t\"warnning\":\"There is no type limit!\"\n\t}");

				} else {
					if (item.previousIndex() > 0) {
						sb.append(",\n");
					}
					sb.append("\t\"" + itemName + "\":{\n");
				}
				hasObject = true;
			}
		}
		// --------------------------------------------------------
		if (!JApiInputExeUtils.isCollection(start.getType())) {
			sb.append("\n}");
		}
		return sb.toString();
	}

	/**
	 * 对注解值对象进行解析
	 * 
	 * @param av           注解值对象
	 * @param defaultValue 默认值
	 * @return 返回相应属性的值
	 */
	private String av(AnnotationValue av, String defaultValue) {
		String value = "";
		if (Objects.isNull(av)) {
			value = defaultValue;
		} else if (av instanceof AnnotationValueList list) {
			for (Object o : list.getParameterValue()) {
				value += o.toString() + ";";
			}
		} else {
			value = Objects.toString(av.getParameterValue());
		}
		return StringUtils.trimToEmpty(StringUtils.substringBeforeLast(value, ";")).replace("\"", "");
	}

	/**
	 * 对注释中标签值的处理
	 * 
	 * @param tag          注释标签
	 * @param defaultValue 默认值
	 * @return 返回相应标签的值
	 */
	private String tv(DocletTag tag, String defaultValue) {
		String tagValue = Objects.isNull(tag) ? defaultValue : StringUtils.substringBefore(tag.getValue(), "{@link");
		return StringUtils.trim(tagValue);
	}

	/**
	 * 对注释中@link的值处理，只取出现的第一个。如：类全路径|集合名称
	 * 
	 * @param tag 注释标签
	 * @return 返回相应标签的值
	 */
	private String dv(DocletTag tag) {
		String value = null;
		for (int i = 0; i < tag.getParameters().size(); i++) {
			String p = tag.getParameters().get(i);
			if ("link".equals(p)) {
				value = tag.getParameters().get(i + 1) + "|";
				if ((i + 2) < tag.getParameters().size()) {
					value += tag.getParameters().get(i + 2);
				}
				if (value.endsWith("|")) {
					value += "Object";
				}
				break;
			}
		}
		return value;
	}

	/**
	 * @link注释中找对应的类型
	 * 
	 * @param value 待处理字符串
	 * @return 返回目标字符串
	 */
	private String dv(String value) {
		String format = "Map|Array|List";
		String x = "Object";
		for (String e : format.split("\\|")) {
			if (value.contains(e)) {
				x = e;
				break;
			}
		}
		return x;
	}

	/**
	 * 显示列表前面的制表符
	 * 
	 * @param i 层级
	 * @return 相应层级制表符
	 */
	private String lv(int i) {
		StringBuffer sb = new StringBuffer("");
		if (i > 0) {
			for (int j = 0; j < i - 1; j++) {
				sb.append("　 ");
			}
			sb.append(TREE_CHAR);
		}
		return sb.toString();
	}

	/**
	 * 拼接本项目中的路径
	 * 
	 * @param src 配置文件input中的输入值
	 * @return 文件绝对路径
	 */
	private String dir(String src) {
		String dir = StringUtils.isBlank(src) ? "src/main/java" : src;
		if (src.startsWith("src" + File.separator)) {
			dir = System.getProperty("user.dir") + File.separator + src;

		} else if (src.startsWith("dir:")) {
			dir = src.replace("dir:", System.getProperty("user.dir"));
		}
		return dir;
	}

	/**
	 * 拼接类的访问路径
	 * 
	 * @param fix       修正规则
	 * @param className 待处理的类名
	 * @return 类的访问路径
	 */
	private String uri(String fix, String className) {
		String[] s = className.split("\\.");
		int n = s.length;// 取一个业务包名和业务类名，按照约定模式生成路径
		String uri = s[n - 3] + "." + StringUtils.fix(fix, s[n - 2] + "." + s[n - 1]);
		return StringUtils.toViewName("/" + uri.replace(".", "/"));
	}
}
